package learn.arc.mybatis;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.transaction.Transaction;
import org.apache.ibatis.transaction.TransactionFactory;
import org.apache.ibatis.transaction.managed.ManagedTransactionFactory;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
import java.util.Properties;

@Slf4j

@Intercepts({
        @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
public class StatementHandlerSqlInterceptor implements Interceptor {
    Transaction transaction;
    //方法拦截
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        //通过StatementHandler获取执行的sql
        StatementHandler executor = (StatementHandler) invocation.getTarget();
        ParameterHandler parameterHandler = executor.getParameterHandler();
        parameterHandler.getParameterObject();
        BoundSql boundSql = executor.getBoundSql();
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();

        if (!CollectionUtils.isEmpty(parameterMappings)) {
            ParameterMapping parameterMapping = parameterMappings.get(0);
            Class clazz = parameterMapping.getClass();
            Field field = clazz.getDeclaredField("configuration");
            field.setAccessible(true);
            Configuration configuration = (Configuration) field.get(parameterMapping);
            Environment environment = configuration.getEnvironment();
            TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
            transaction = transactionFactory.newTransaction(environment.getDataSource(), null, false);
            Statement statement = prepareStatement(executor);
            transaction.close();
            log.info(statement.toString());

        }

        Object proceed = invocation.proceed();
        return proceed;
    }

    //获取到拦截的对象，底层也是通过代理实现的，实际上是拿到一个目标代理对象
    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    //获取设置的阈值等参数
    @Override
    public void setProperties(Properties properties) {
    }


    private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {
        if (environment == null || environment.getTransactionFactory() == null) {
            return new ManagedTransactionFactory();
        }
        return environment.getTransactionFactory();
    }

    private Statement prepareStatement(StatementHandler handler) throws SQLException {
        Statement stmt;
        Connection connection = transaction.getConnection();
        stmt = handler.prepare(connection, transaction.getTimeout());
        handler.parameterize(stmt);
        return stmt;
    }


}
